package cloud.pangui.core.lock.executor.impl;

import cloud.pangui.core.lock.executor.LockExecutor;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.Collections;

/**
 * @Auther hepangui
 * @Date 2020/10/9 0009
 */
public class RedisTemplateLockExecutor implements LockExecutor {

	/**
	 * setnx key不存在时set，设置的值为本身生成的一个uuid。加过期时间时防止因为事故造成未解锁时死锁
	 */
	private static final RedisScript<String> SCRIPT_LOCK = new DefaultRedisScript<>("return redis.call('set',KEYS[1],ARGV[1],'NX','PX',ARGV[2])", String.class);

	/**
	 * 解铃还须系铃人，杜绝下述状况
	 * A加锁，由于长期执行，锁过期了。
	 * 此时客户端B尝试加锁成功，然后客户端A解锁，则将客户端B的锁给解除了。
	 */
	private static final RedisScript<String> SCRIPT_RELEASE = new DefaultRedisScript<>("if redis.call('get',KEYS[1]) == ARGV[1] then return tostring(redis.call('del', KEYS[1])==1) else return 'false' end", String.class);

	/**
	 * 加锁成功时redis的返回
	 */
	private static final String LOCK_SUCCESS = "OK";

	@Setter
	@Autowired
	private RedisTemplate redisTemplate;


	@Override
	public boolean lock(String lockKey, String selfMark, long expire){
		Object lockResult = redisTemplate.execute(SCRIPT_LOCK,
				redisTemplate.getStringSerializer(),
				redisTemplate.getStringSerializer(),
				Collections.singletonList(lockKey),
				selfMark, String.valueOf(expire));
		return LOCK_SUCCESS.equals(lockResult);
	}


	@Override
	public boolean release(String key , String selfMark) {
		Object releaseResult = redisTemplate.execute(SCRIPT_RELEASE,
				redisTemplate.getStringSerializer(),
				redisTemplate.getStringSerializer(),
				Collections.singletonList(key),
				selfMark);
		return Boolean.valueOf(releaseResult.toString());
	}

}